package com.guojing.jl.myspring.demo.spring;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.guojing.jl.myspring.demo.service.AppConfig;
import com.guojing.jl.myspring.demo.spring.annotation.Autowired;
import com.guojing.jl.myspring.demo.spring.annotation.Compent;
import com.guojing.jl.myspring.demo.spring.annotation.CompentScan;
import com.guojing.jl.myspring.demo.spring.annotation.Scope;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * spring容器
 */
public class MyApplicationContext {

    private Map<String, BeanDefinition> beanDefinitionNameMap = new ConcurrentHashMap<>();
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();

    private List<BeanPostProcessor> postProcessorList = new ArrayList<>();

    /**
     * 配置类的类型
     */
    private Class configClass;


    public MyApplicationContext(Class<AppConfig> configClass) {
        this.configClass = configClass;


        //配置类是否有compentScan注解
        if (configClass.isAnnotationPresent(CompentScan.class)) {
            CompentScan compentScanAnnotation = configClass.getAnnotation(CompentScan.class);

            /**
             * D:\myprogramfile\Java\jdk1.8.0_161\jre\lib\javaws.jar;
             * D:\myprogramfile\Java\jdk1.8.0_161\jre\lib\jce.jar;
             * D:\myprogramfile\Java\jdk1.8.0_161\jre\lib\rt.jar;
             * G:\jl_project\src\idea\springboot-learning\springboot-myself\target\classes com.guojing.jl.myspring.demo.MainTest
             *
             *  以上是本地测试的classpath，最终程序运行的时候也是从上面的路径中查找对应的配置类,所以我们需要构造这个路径出来
             */

            //扫描路径
            String scanPackge = compentScanAnnotation.value();
            //相对路径: com/guojing/jl/myspring/demo
            scanPackge = scanPackge.replaceAll("\\.", "/");

            ClassLoader classLoader = this.getClass().getClassLoader();
            URL resource = classLoader.getResource(scanPackge);
            //路径: /G:/jl_project/src/idea/springboot-learning/springboot-myself/target/classes/com/guojing/jl/myspring/demo
            String path = resource.getFile();

            File file = new File(path);
            try {
                List<File> files = FileUtil.loopFiles(file);
                for (File f : files) {
                    String fileName = f.getAbsolutePath();
                    if (fileName.endsWith(".class")) {
                        //G:\jl_project\src\idea\springboot-learning\springboot-myself\target\classes\com\guojing\jl\myspring\demo\spring\MyApplicationContext.class

                        String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
                        className = className.replace("\\", ".");

                        Class<?> aClass = classLoader.loadClass(className);
                        //判断Bean是否有compent注解,有的话加入spring容器
                        if(aClass.isAnnotationPresent(Compent.class)){

                            //BeanPostProcessor
                            if(BeanPostProcessor.class.isAssignableFrom(aClass)){
                                BeanPostProcessor postProcessor = (BeanPostProcessor) aClass.newInstance();
                                postProcessorList.add(postProcessor);
                            }

                            Compent compentAnn = aClass.getAnnotation(Compent.class);
                            String beanName = compentAnn.value();

                            if(StrUtil.isBlank(beanName)){
                                beanName = Introspector.decapitalize(aClass.getSimpleName());
                            }

                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setType(aClass);
                            beanDefinition.setBeanName(beanName);

                            if(aClass.isAnnotationPresent(Scope.class)){
                                Scope scopeAnno = aClass.getAnnotation(Scope.class);
                                beanDefinition.setSingle(scopeAnno.value());
                            }else {
                                beanDefinition.setSingle(true);
                            }
                            beanDefinitionNameMap.put(beanName, beanDefinition);
                        }

                    }

                }

                //生成单例Bean
                buildSingBean();

            } catch (Exception e) {
                e.printStackTrace();
            }

        }

    }

    private void buildSingBean() {
        for (String beanName : beanDefinitionNameMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionNameMap.get(beanName);
            if(beanDefinition.isSingle()){
                Object bean = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName, bean);
            }
        }

    }

    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        try{
            Class type = beanDefinition.getType();
            Object instance = type.getConstructor().newInstance();

            //依赖注入
            for (Field field : type.getDeclaredFields()) {
                if(field.isAnnotationPresent(Autowired.class)){
                    field.setAccessible(true);

                    //调用getBean 递归创建
                    field.set(instance , getBean(field.getName()));
                }
            }

            //aware回调
            if(instance instanceof  BeanNameAware){
                BeanNameAware beanNameAware = (BeanNameAware)instance;
                beanNameAware.setName(beanName);
            }

            //初始化之前
            for (BeanPostProcessor postProcessor : postProcessorList) {
                instance = postProcessor.postProcessBeforeInitialization(beanName, instance);
            }

            //初始化
            if(instance instanceof InitializingBean){
                InitializingBean initializingBean = (InitializingBean)instance;
                initializingBean.afterPropertieSet();
            }

            //初始化之后
            for (BeanPostProcessor postProcessor : postProcessorList) {
                instance =  postProcessor.postProcessAfterInitialization(beanName, instance);
            }

            //初始化后 AOP

            return instance;

        }catch (Exception e){
            e.printStackTrace();
        }

        return null;
    }

    public Object getBean(String name) throws Exception {

        BeanDefinition beanDefinition = beanDefinitionNameMap.get(name);
        if(beanDefinition == null){
            throw new IllegalArgumentException();
        }else{
            boolean single = beanDefinition.isSingle();
            if(single){
                Object bean = singletonObjects.get(name);
                if(bean == null){
                    bean = createBean(name, beanDefinition);
                    singletonObjects.put(name, bean);
                }
                return bean;
            }else{
                //多例
                return createBean(name, beanDefinition);
            }

        }
    }
}
